package com.tianji.learning.service.impl;

import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author author
 * @since 2023-11-07
 */
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final ILearningLessonService lessonService;
    private final CourseClient courseClient;
    private final LearningRecordDelayTaskHandler taskHandler;
    private final RabbitMqHelper mqHelper;

    /**
     * 查询当前用户指定课程的学习进度
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        //1. 获取当前登录用户Id
        Long userId = UserContext.getUser();
        //2.查询课表信息，userId和 courseId
        LearningLesson lesson = lessonService.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if (lesson == null) {
            throw new BizIllegalException("该课程未加入课表！");
        }
        //3. 查询学习记录 条件lesson_id 和 userId
        List<LearningRecord> recordList = this.lambdaQuery()
                .eq(LearningRecord::getLessonId, lesson.getId())
                .eq(LearningRecord::getUserId, userId)
                .list();
        //4. 封装返回
        LearningLessonDTO dto = new LearningLessonDTO();
        dto.setId(lesson.getId());
        dto.setLatestSectionId(lesson.getLatestSectionId());
        List<LearningRecordDTO> dtoList = BeanUtils.copyList(recordList, LearningRecordDTO.class);
        dto.setRecords(dtoList);
        return dto;
    }

    /**
     * 提交学习记录
     * @param formDTO
     */
    @Override
    public void addLearningRecord(LearningRecordFormDTO formDTO) {
        //1. 获取当前登录用户Id
        Long userId = UserContext.getUser();
        //2.处理学习记录
        boolean isFinished = false; //是否完成学习
        if (formDTO.getSectionType() == SectionType.EXAM){
            //2.1提交考试记录
            isFinished = handleExamRecord(userId,formDTO);
        }else{
            //2.2提交视频播放记录
            isFinished = handleVideoRecord(userId,formDTO);
        }
        if (!isFinished){  //如果本小节 不是第一次学完，不用处理课表数据
            return;
        }
        //3.处理课表数据
        handleLessonData(formDTO);
    }

    /** 处理课表数据
     * @param formDTO
     */
    private void handleLessonData(LearningRecordFormDTO formDTO) {
        //1.查询旧的课表信息，条件：lesson_id主键
        LearningLesson lesson = lessonService.getById(formDTO.getLessonId());
        if (lesson == null){
            throw new BizIllegalException("该课程不存在！");
        }
        //2.判断是否是第一次学完 isFinished是不是为true
        boolean allFinished = false; //所有小节是否都学完
            //3.远程调用课程服务，得到课程 总小节数 信息
            CourseFullInfoDTO cinfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
            if (cinfo == null){
                throw new BizIllegalException("课程信息不存在！");
            }
            Integer sectionNum = cinfo.getSectionNum();  //该课程 小节总数
            //4.如果本小节是第一次学完，判断该用户对该课程下全部小节是否学完
            Integer learnedSections = lesson.getLearnedSections(); //该用户已经学过的小节数
            allFinished = learnedSections + 1 >= sectionNum; //是否全部学完
        //5.更新课表数据
        lessonService.lambdaUpdate()
                .set(lesson.getLearnedSections() == 0,LearningLesson::getStatus, LessonStatus.LEARNING)
                .set(allFinished,LearningLesson::getStatus, LessonStatus.FINISHED)
                .set(LearningLesson::getLatestSectionId,formDTO.getSectionId())
                .set(LearningLesson::getLatestLearnTime,formDTO.getCommitTime())
                //.set(isFinished,LearningLesson::getLearnedSections,lesson.getLearnedSections() + 1)
                .setSql("learned_sections = learned_sections + 1") //手动拼接sql
                .eq(LearningLesson::getId,lesson.getId())
                .update();
    }

    /**
     * 处理视频播放记录
     * @param userId
     * @param formDTO
     */
    private boolean handleVideoRecord(Long userId, LearningRecordFormDTO formDTO) {
        //1。查询旧的视频记录，判断记录是否存在，条件：lesson_id、section_id、user_id
        LearningRecord learningRecord = queryOldRecord(formDTO.getLessonId(),formDTO.getSectionId());
//        LearningRecord learningRecord = this.lambdaQuery()
//                .eq(LearningRecord::getLessonId, formDTO.getLessonId())
//                .eq(LearningRecord::getSectionId, formDTO.getSectionId())
//                .one();
        //2.如果不存在，则新增学习记录
        if (learningRecord == null){
            //1.dto 转 po，因为po对接数据库
            LearningRecord record = BeanUtils.copyBean(formDTO, LearningRecord.class);
            //单独处理 userId、finished、finishTime
            record.setUserId(userId);
            //2.保存学习记录到 learning_record
            boolean result = this.save(record);
            if (!result){
                throw new DbException("新增视频学习记录失败！");
            }
            return false;  //没有完成，返回false
        }
        //3.如果存在，则更新学习记录 learning_record 更新 moment
        //判断本小节是否是第一次学完  1.旧状态是否完成；2.本次播放的进度是否超出50%
        boolean isFinished = !learningRecord.getFinished() && formDTO.getMoment() * 2 > formDTO.getDuration();

        //不是第一次学完，添加到延迟任务队列
        if (!isFinished){
            LearningRecord record = new LearningRecord();
            record.setLessonId(formDTO.getLessonId());
            record.setSectionId(formDTO.getSectionId());
            record.setMoment(formDTO.getMoment());
            record.setFinished(learningRecord.getFinished());
            record.setId(learningRecord.getId());
            taskHandler.addLearningRecordTask(record);//添加到延迟任务队列
            return false;
        }

        //是第一次学完，更新学习记录
        //update learning_record set finished = ture,moment = xxx,finish_time = xxx where id = xxx
        boolean result = this.lambdaUpdate()
                .set(LearningRecord::getMoment, formDTO.getMoment())
                .set(isFinished, LearningRecord::getFinished, true)
                .set(LearningRecord::getFinishTime, formDTO.getCommitTime())
                .eq(LearningRecord::getId, learningRecord.getId())
                .update();
        if (!result){
            throw new DbException("更新视频学习记录失败！");
        }

        //发送消息到mq，累加积分
         mqHelper.send(
                 MqConstants.Exchange.LEARNING_EXCHANGE,
                 MqConstants.Key.LEARN_SECTION,
                 userId
         );


        //清理缓存
        taskHandler.cleanRecordCache(formDTO.getLessonId(), formDTO.getSectionId());

        return true;
    }

    private LearningRecord queryOldRecord(Long lessonId, Long sectionId) {
        //1.查询缓存
        LearningRecord cache = taskHandler.readRecordCache(lessonId, sectionId);
        //2.如果命中，直接返回
        if (cache!= null){
            return cache;
        }
        //3.如果未命中，查询数据库
        LearningRecord dbRecord = this.lambdaQuery()
                .eq(LearningRecord::getLessonId,lessonId)
                .eq(LearningRecord::getSectionId,sectionId)
                .one();
        if (dbRecord == null){
            return null;
        }
        //上面的判断 讲义中没写 不写可能会报空指针异常
        //4.放入缓存
        taskHandler.writeRecordCache(dbRecord);
        return dbRecord;
    }

    /**
     * 处理考试记录
     * @param userId
     * @param formDTO
     */
     private boolean handleExamRecord(Long userId, LearningRecordFormDTO formDTO) {
         //1.dto 转 po，因为po对接数据库
         LearningRecord record = BeanUtils.copyBean(formDTO, LearningRecord.class);
         //单独处理 userId、finished、finishTime
         record.setUserId(userId);
         record.setFinished(true); //提交考试记录，代表本小节已完成
         record.setFinishTime(formDTO.getCommitTime());
         //2.保存学习记录到 learning_record
         boolean result = this.save(record);
         if (!result){
             throw new DbException("新增考试记录失败！");
         }
         return true;
    }
}
